// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/parser/parser.h>
#include <cantor/transform/ddls/alter_table_spec.h>
#include <cantor/transform/exprs/range_var.h>
#include <cantor/transform/clause/table_name.h>
#include <cantor/transform/expr.h>
#include <cantor/transform/dqls/where_clause.h>
#include <cantor/transform/dmls/assignment.h>
#include <cantor/transform/ddls/column_def.h>
#include <cantor/transform/ddls/constraint.h>
#include <cantor/transform/ddls/partition_range.h>
#include <cantor/transform/ddls/table_option.h>

namespace cantor::transform {

    void TransAlterTableSpec::resolve_ast(const cantor::parser::AlterTableSpec*node) {
        if (!node) {
            status = turbo::data_loss_error("no expr");
            return;
        }

        alter_table_spec.set_spec_type(node->spec_type);
        alter_table_spec.set_column_name(node->column_name.to_string());
        /// table_options
        auto mtable_options = alter_table_spec.mutable_table_options();
        mtable_options->Reserve(node->table_options.size());
        for (size_t i = 0; i < node->table_options.size(); ++i) {
            TransTableOption o;
            o.resolve_ast(node->table_options[i]);
            status = o.check();
            if (!status.ok()) {
                return;
            }
            *mtable_options->Add() = std::move(o.table_option);
        }
        /// new_table_name
        status = TransTableName::resolve_to(node->new_table_name, alter_table_spec.mutable_new_table_name());
        if (!status.ok()) {
            return;
        }
        /// new_columns
        auto new_columns_ptr = alter_table_spec.mutable_new_columns();
        new_columns_ptr->Reserve(node->new_columns.size());
        for (size_t i = 0; i < node->new_columns.size(); ++i) {
            TransColumnDef transer;
            transer.resolve_ast(node->new_columns[i]);
            status = transer.check();
            if (!status.ok()) {
                return;
            }
            *new_columns_ptr->Add() = std::move(transer.column_def);
        }

        /// set_list
        auto set_list_ptr = alter_table_spec.mutable_set_list();
        set_list_ptr->Reserve(set_list_ptr->size());
        for (size_t i = 0; i < set_list_ptr->size(); ++i) {
            TransAssignment trans;
            trans.resolve_ast(node->set_list[i]);
            status = trans.check();
            if (!status.ok()) {
                return;
            }
            *set_list_ptr->Add() = std::move(trans.assignment);
        }
        /// where
        if (node->where) {
            status = TransWhereClause::resolve_to(node->where, alter_table_spec.mutable_where());
            if (!status.ok()) {
                return;
            }
        }
        /// new_constraints
        auto new_constraints_ptr = alter_table_spec.mutable_new_constraints();
        new_constraints_ptr->Reserve(new_constraints_ptr->size());
        for (size_t i = 0; i < new_constraints_ptr->size(); ++i) {
            TransConstraint trans;
            trans.resolve_ast(node->new_constraints[i]);
            status = trans.check();
            if (!status.ok()) {
                return;
            }
            *new_constraints_ptr->Add() = std::move(trans.constraint);
        }

        /// index_name
        alter_table_spec.set_index_name(node->index_name.to_string());
        /// resource_tag
        alter_table_spec.set_resource_tag(node->resource_tag.to_string());

        alter_table_spec.set_force(node->force);
        alter_table_spec.set_is_virtual_index(node->is_virtual_index);
        alter_table_spec.set_is_unique_indicator(node->is_unique_indicator);
        if (node->partition_range) {
            TransPartitionRange trans;
            trans.resolve_ast(node->partition_range);
            status = trans.check();
            if (!status.ok()) {
                return;
            }
            *alter_table_spec.mutable_partition_range() = std::move(trans.partition_range);
        }
        set_proto_location(node, alter_table_spec.mutable_location());
        set_proto_status(alter_table_spec.mutable_status());
    }

    const turbo::Status &TransAlterTableSpec::check() {
        if (!status.ok()) {
            return status;
        }
        if (!alter_table_spec.has_location()) {
            make_missing_required_field("location");
            return status;
        }
        return status;
    }

    turbo::Status TransAlterTableSpec::resolve_to(turbo::Nonnull<const cantor::parser::AlterTableSpec*> arg, turbo::Nonnull<pb::ast::AlterTableSpec *>out) {
        TransAlterTableSpec trans;
        trans.resolve_ast(arg);
        auto status = trans.check();
        if (!status.ok()) {
            return status;
        }
        *out = std::move(trans.alter_table_spec);
        return status;
    }
}  // namespace cantor::transform
